#pragma once

#include "base.h"
#include "../impl/ma.h"

namespace mdtx::online::updater
{
    template <typename T>
    class sum : public updater_unary1<T>,
                public impl::sum<T>
    {
        using base = updater_unary1<T>;
        using ibase = impl::sum<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;
        using ibase::roll_impl;

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in));
        }

        void remove(tuple_in const &in) override
        {
            remove_impl(std::get<0>(in));
        }

        void roll(T x, T y)
        {
            roll_impl(x, y);
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->s);
        }
    };

    template <typename T>
    class sma : public updater_unary1<T>,
                public impl::sma<T>
    {
        using base = updater_unary1<T>;
        using ibase = impl::sma<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;
        using ibase::roll_impl;

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in));
        }

        void remove(tuple_in const &in) override
        {
            remove_impl(std::get<0>(in));
        }

        void roll(T x, T y)
        {
            roll_impl(x, y);
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->m);
        }
    };

    template <typename T>
    class wma : public updater_unary1<T>,
                public impl::wma<T>
    {
        using base = updater_unary1<T>;
        using ibase = impl::wma<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;
        using ibase::roll_impl;

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in));
        }

        void remove(tuple_in const &in) override
        {
            remove_impl(std::get<0>(in));
        }

        void roll(T x, T y)
        {
            roll_impl(x, y);
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->wxsum / this->tot_w);
        }
    };

    template <typename T>
    class wsma : public updater_weighted1<T>,
                 public impl::wsma<T>
    {
        using base = updater_weighted1<T>;
        using ibase = impl::wsma<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in), std::get<1>(in));
        }

        void remove(tuple_in const &in) override
        {
            remove_impl(std::get<0>(in), std::get<1>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->m);
        }
    };

    template <typename T>
    class ewma : public updater_unary1<T>,
                 public impl::ewma<T>
    {
        using base = updater_unary1<T>;
        using ibase = impl::ewma<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::ibase;
        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;
        using ibase::roll_impl;

        explicit ewma(ptrdiff_t period)
            : ibase(T(2) / (period + 1))
        {
        }

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in));
        }

        void remove(tuple_in const &in) override
        {
            remove_impl(std::get<0>(in));
        }

        void roll(T x, T y)
        {
            roll_impl(x, y);
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->wsum / this->tot_w);
        }
    };

    template <typename T>
    class ema : public updater_unary1<T>,
                public impl::ema<T>
    {
        using base = updater_unary1<T>;
        using ibase = impl::ema<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::ibase;
        using ibase::insert_impl;
        using ibase::reset_impl;

        explicit ema(ptrdiff_t period)
            : ibase(T(2) / (period + 1))
        {
        }

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->m);
        }
    };

    template <typename T>
    class ema_evw : public updater_weighted1<T>,
                    public impl::ema_evw<T>
    {
        using base = updater_weighted1<T>;
        using ibase = impl::ema_evw<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::reset_impl;

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in), std::get<1>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->m);
        }
    };

    template <typename T, size_t Order>
    class xema : public updater_base<T, T, 1, Order>,
                 public impl::xema<T, Order>
    {
        using base = updater_base<T, T, 1, Order>;
        using ibase = impl::xema<T, Order>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::ibase;
        using ibase::insert_impl;
        using ibase::reset_impl;

        explicit xema(ptrdiff_t period)
            : ibase(T(2) / (period + 1))
        {
        }

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return mk_tuple(this->m);
        }
    };
}
